package com.usv.comparepic;

import com.usv.drawer.DifferenceDrawer;
import com.usv.drawer.impl.DifferenceFileDrawer;
import com.usv.point.ComparatorX;
import com.usv.point.ComparatorY;
import com.usv.point.Point;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import static java.lang.Math.abs;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class ComparePic {

    public static Color COLOR_DIFF = Color.RED;
    public static Color COLOR_IGNORE = Color.GREEN;
    public static int PERCENT_TOLERANCE = 10;
    private DifferenceDrawer diffDrawer = new DifferenceFileDrawer();
    private BufferedImage resultPic;

    public boolean compareTwoImages(BufferedImage bufImgOne, BufferedImage bufImgTwo, List<Rectangle> ignoreList) {

        if (bufImgOne == null || bufImgTwo == null) {
            throw new IllegalArgumentException("No pictures for compare.");
        }
        if (isDifferentSize(bufImgOne, bufImgTwo)) {
            System.out.println("Pictures are of different sizes.");
            return false;
        }

        resultPic = new BufferedImage(bufImgTwo.getWidth(), bufImgTwo.getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics g = resultPic.createGraphics();
        g.drawImage(bufImgTwo, 0, 0, null);

        if (ignoreList != null) {
            for (Rectangle r : ignoreList) {
                diffDrawer.fillIgnore(bufImgOne, r);
                diffDrawer.fillIgnore(bufImgTwo, r);
                Point start = new Point(r.x, r.y);
                Point end = new Point(r.x + r.width, r.y + r.height);
                diffDrawer.drawFrame(resultPic, start, end, COLOR_IGNORE);
            }
        }

        List<Point> diffPoints = new ArrayList<>();
        int imgTwoHt = bufImgTwo.getHeight();
        int imgTwoWt = bufImgTwo.getWidth();
        for (int x = 0; x < imgTwoWt; x++) {
            for (int y = 0; y < imgTwoHt; y++) {
                Point point = new Point(x, y);
                if (isDifferentPixel(bufImgOne, bufImgTwo, point, PERCENT_TOLERANCE)) {
                    diffPoints.add(point);
                }
            }
        }

        if (diffPoints.isEmpty()) {
            System.out.println("Both of the pictures look the same.");
            return true;
        }

        findSelections(diffPoints, bufImgTwo);
        System.out.println("Pictures are different.");
        return false;

    }

    private static boolean isDifferentSize(BufferedImage bufImgOne, BufferedImage bufImgTwo) {

        int imgOneHt = bufImgOne.getHeight();
        int imgTwoHt = bufImgTwo.getHeight();
        int imgOneWt = bufImgOne.getWidth();
        int imgTwoWt = bufImgTwo.getWidth();
        return imgOneHt != imgTwoHt || imgOneWt != imgTwoWt;
    }

    private boolean isDifferentPixel(BufferedImage bufImgOne, BufferedImage bufImgTwo,
            Point point, int percent) {
        int x = point.getX();
        int y = point.getY();
        boolean result = false;

        int tolerance = 255 * percent / 100;

        int pixel1 = bufImgOne.getRGB(x, y);
        int pixel2 = bufImgTwo.getRGB(x, y);

        int alpha1 = (pixel1 >> 24) & 0xff;
        int red1 = (pixel1 >> 16) & 0xff;
        int green1 = (pixel1 >> 8) & 0xff;
        int blue1 = (pixel1 >> 0) & 0xff;

        int alpha2 = (pixel2 >> 24) & 0xff;
        int red2 = (pixel2 >> 16) & 0xff;
        int green2 = (pixel2 >> 8) & 0xff;
        int blue2 = (pixel2 >> 0) & 0xff;

        if (abs(alpha1 - alpha2) > tolerance
                || abs((red1 - red2)) > tolerance
                || abs(green1 - green2) > tolerance
                || abs(blue1 - blue2) > tolerance) {
            result = true;
        }
        return result;
    }

    private void findSelections(List<Point> diffPoints, BufferedImage bufImgTwo) {

        List<Point> list1 = new ArrayList<>();
        List<Point> list2 = new ArrayList<>();
        List<Point> list3 = new ArrayList<>();
        int xStart = Collections.min(diffPoints, new ComparatorX()).getX();
        int yStart = Collections.min(diffPoints, new ComparatorY()).getY();
        int x1 = xStart;
        int x2 = xStart;
        int x3 = xStart;
        int y1 = yStart;
        int y2 = yStart;
        int y3 = yStart;
        int temp;
        Point nextPoint;

        if (!diffPoints.isEmpty()) {
            Collections.sort(diffPoints, new ComparatorX());
            Iterator<Point> iterator = diffPoints.iterator();

            while (iterator.hasNext()) {
                temp = x1;
                nextPoint = iterator.next();
                x1 = nextPoint.getX();
                if (x1 - temp > 2) {
                    break;
                }
                list1.add(nextPoint);
            }
        }

        if (!list1.isEmpty()) {
            y1 = Collections.min(list1, new ComparatorY()).getY();
            Collections.sort(list1, new ComparatorY());
            Iterator<Point> iterator1 = list1.iterator();

            while (iterator1.hasNext()) {
                temp = y2;
                nextPoint = iterator1.next();
                y2 = nextPoint.getY();
                if (y2 > y1) {
                    if (y2 - temp > 2) {
                        y2 = temp;
                        break;
                    }
                    list2.add(nextPoint);
                }
            }
        }

        if (!list2.isEmpty()) {
            x2 = Collections.min(list2, new ComparatorX()).getX();
            Collections.sort(list2, new ComparatorX());
            Iterator<Point> iterator2 = list2.iterator();

            while (iterator2.hasNext()) {
                temp = x3;
                nextPoint = iterator2.next();
                x3 = nextPoint.getX();
                if (x2 > x3) {
                    if (x3 - temp > 2) {
                        x3 = temp;
                        break;
                    }
                    list3.add(nextPoint);
                }
            }
        }
        if (!list3.isEmpty()) {
            y1 = Collections.min(list3, new ComparatorY()).getY();
            Collections.sort(list3, new ComparatorY());
            Iterator<Point> iterator3 = list3.iterator();

            while (iterator3.hasNext()) {
                temp = y3;
                nextPoint = iterator3.next();
                y3 = nextPoint.getY();
                if (y3 > y2) {
                    if (y3 - temp > 2) {
                        y3 = temp;
                        break;
                    }
                }
            }

        }

        Point startPoint = new Point(x2, y1);
        Point endPoint = new Point(x3, y2);

        diffDrawer.drawFrame(resultPic, startPoint, endPoint, COLOR_DIFF);

        if (!diffPoints.isEmpty() && !list1.isEmpty()) {
            diffPoints.removeAll(list1);
            if (!diffPoints.isEmpty()) {
                findSelections(diffPoints, bufImgTwo);
            }
        }
        if (!list1.isEmpty() && !list2.isEmpty()) {
            list1.removeAll(list2);
            if (!list1.isEmpty()) {
                findSelections(list1, bufImgTwo);
            }
        }

        if (!list2.isEmpty() && !list3.isEmpty()) {
            list2.removeAll(list3);
            if (!list2.isEmpty()) {
                findSelections(list2, bufImgTwo);
            }
        }
    }
}
